﻿using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace Microsoft.EntityFrameworkCore
{
    public static partial class PropertyBuilderExtensions
    {
        #region add enum conversion

        /// <summary>
        /// 为 <paramref name="propertyBuilder"/>添加 EnumToNumberConverter&lt;<typeparamref name="TProperty"/>, int&gt;值转换器
        /// </summary>
        /// <typeparam name="TProperty">enum type</typeparam>
        /// <param name="propertyBuilder">property builder</param>
        /// <returns><see cref="PropertyBuilder{TProperty}"/></returns>
        public static PropertyBuilder<TProperty> HasEnumToIntConversion<TProperty>(this PropertyBuilder<TProperty> propertyBuilder)
            where TProperty : struct
        {
            if (!typeof(TProperty).IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with enum types.");
            }

            return propertyBuilder.HasConversion(new EnumToNumberConverter<TProperty, int>());
        }

        /// <summary>
        /// 为 <paramref name="propertyBuilder"/>添加 EnumToNumberConverter&lt;<typeparamref name="TProperty"/>, <typeparamref name="TNumber"/>&gt;值转换器
        /// </summary>
        /// <typeparam name="TProperty">enum type</typeparam>
        /// <typeparam name="TNumber">number type</typeparam>
        /// <param name="propertyBuilder">property builder</param>
        /// <returns><see cref="PropertyBuilder{TProperty}"/></returns>
        public static PropertyBuilder<TProperty> HasEnumToNumberConversion<TProperty, TNumber>(this PropertyBuilder<TProperty> propertyBuilder)
            where TProperty : struct
            where TNumber : struct
        {
            if (!typeof(TProperty).IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with enum types.");
            }

            return propertyBuilder.HasConversion(new EnumToNumberConverter<TProperty, TNumber>());
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/> 添加 EnumToStringConverter&lt;<typeparamref name="TProperty"/>&gt; 值转换器
        /// </summary>
        /// <typeparam name="TProperty">enum type</typeparam>
        /// <param name="propertyBuilder">property builder</param>
        /// <returns><see cref="PropertyBuilder{TProperty}"/></returns>
        public static PropertyBuilder<TProperty> HasEnumToStringConversion<TProperty>(this PropertyBuilder<TProperty> propertyBuilder)
            where TProperty : struct
        {
            if (!typeof(TProperty).IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with enum types.");
            }

            return propertyBuilder.HasConversion(new EnumToStringConverter<TProperty>());
        }

        #endregion //end enum conversion

        #region add enum nullable conversion 

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;<typeparamref name="TProperty"/>, string&gt; 可空值转换器
        /// </summary>
        /// <typeparam name="TProperty">nullable enum type</typeparam>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <returns></returns>
        public static PropertyBuilder<TProperty> HasEnumToStringNullableConversion<TProperty>(this PropertyBuilder<TProperty> propertyBuilder)
        {
            if (!typeof(TProperty).IsNullableValueType() || !typeof(TProperty).UnwrapNullableType().IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with nullable enum types.");
            }

            return propertyBuilder.HasConversion(new NullableEnumToStringConverter<TProperty>());
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;<typeparamref name="TProperty"/>, int?&gt; 可空值转换器
        /// </summary>
        /// <typeparam name="TProperty">nullable enum type</typeparam>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <returns></returns>
        public static PropertyBuilder<TProperty> HasEnumToIntNullableConversion<TProperty>(this PropertyBuilder<TProperty> propertyBuilder)
        {
            return propertyBuilder.HasEnumToNumerNullableConversion<TProperty, int?>();
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;<typeparamref name="TProperty"/>, <typeparamref name="TNumerNullable"/>&gt; 可空值转换器
        /// </summary>
        /// <typeparam name="TProperty">nullable enum type</typeparam>
        /// <typeparam name="TNumerNullable">nullablenumberic type</typeparam>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <returns></returns>
        public static PropertyBuilder<TProperty> HasEnumToNumerNullableConversion<TProperty, TNumerNullable>(this PropertyBuilder<TProperty> propertyBuilder)
        {

            if (!typeof(TProperty).IsNullableValueType() || !typeof(TProperty).UnwrapNullableType().IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with nullable enum types.");
            }

            if (!typeof(TNumerNullable).IsNullableValueType())
            {
                throw new InvalidOperationException("This converter can only be used with nullable numberic types.");
            }

            return propertyBuilder.HasConversion(new NullableEnumToNumberConverter<TProperty, TNumerNullable>());
        }

        #endregion //end of add enum nullable conversion

        #region add bool? conversion

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;bool?, string&gt; 可空值转换器
        /// </summary>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <param name="falseValue">false vlaue</param>
        /// <param name="trueValue">true value</param>
        /// <param name="isIgnoreCase">Whether to ignore case</param>
        /// <returns></returns>
        public static PropertyBuilder<bool?> HasBoolToStringNullableConversion(this PropertyBuilder<bool?> propertyBuilder, string falseValue = "N", string trueValue = "Y", bool isIgnoreCase = true)
        {
            propertyBuilder.HasConversion(new NullableBoolToStringConverter(trueValue, falseValue, isIgnoreCase));
            return propertyBuilder;
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;bool?, int?&gt; trueValue:1,falseValue:0 可空值转换器
        /// </summary>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <returns></returns>
        public static PropertyBuilder<bool?> HasBoolToIntNullableConversion(this PropertyBuilder<bool?> propertyBuilder)
        {
            return propertyBuilder.HasBoolToNumberNullableConversion(0, 1);
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;bool?, <typeparamref name="TNumer"/>?&gt; trueValue:1,falseValue:0 可空值转换器
        /// </summary>
        /// <typeparam name="TNumer">值类型</typeparam>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <returns></returns>
        public static PropertyBuilder<bool?> HasBoolToZeroOneNullableConversion<TNumer>(this PropertyBuilder<bool?> propertyBuilder)
            where TNumer : struct
        {
            var falseValue = (TNumer)(object)0;
            var trueValue = (TNumer)(object)1;

            return propertyBuilder.HasBoolToNumberNullableConversion(falseValue, trueValue);
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;bool?, <typeparamref name="TNumer"/>?&gt; 自定义真假转换值的可空值转换器
        /// </summary>
        /// <typeparam name="TNumer">值类型</typeparam>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <param name="falseValue">false Value</param>
        /// <param name="trueValue">true value</param>
        /// <returns></returns>
        public static PropertyBuilder<bool?> HasBoolToNumberNullableConversion<TNumer>(this PropertyBuilder<bool?> propertyBuilder, TNumer falseValue, TNumer trueValue)
            where TNumer : struct
        {
            return propertyBuilder.HasConversion(new NullableBoolToNumberConverter<TNumer>(falseValue, trueValue));
        }

        #endregion

        #region add bool conversion

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 ValueConverter&lt;bool, string&gt;值转换器
        /// </summary>
        /// <param name="propertyBuilder"> property builder</param>
        /// <param name="falseValue">fasle value</param>
        /// <param name="trueValue">true value</param>
        /// <param name="isIgnoreCase">是否忽略大小写</param>
        /// <returns></returns>
        public static PropertyBuilder<bool> HasBoolToStringConversion(this PropertyBuilder<bool> propertyBuilder, string falseValue = "N", string trueValue = "Y", bool isIgnoreCase = true)
        {
            return propertyBuilder.HasConversion(new CustomBoolToStringConverter(falseValue, trueValue, isIgnoreCase));
        }

        #endregion //end bool conversion

    }

    public static partial class PropertyBuilderExtensions
    {
        /// <summary>
        /// 为<paramref name="propertyBuilder"/> 添加 <typeparamref name="TProperty"/> 转 ShortName String 值转换器
        /// </summary>
        /// <typeparam name="TProperty">enum type</typeparam>
        /// <param name="propertyBuilder">property builder</param>
        /// <returns><see cref="PropertyBuilder{TProperty}"/></returns>
        public static PropertyBuilder<TProperty> HasEnumToShortNameStringConversion<TProperty>(this PropertyBuilder<TProperty> propertyBuilder)
            where TProperty : struct
        {
            if (!typeof(TProperty).IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with enum types.");
            }

            var (toStr, toEnum) = EnumConveterHelper.CreateShortNameExpression<TProperty>();

            return propertyBuilder.HasConversion(new CustomToStringConverter<TProperty>(toStr, toEnum));
        }

        /// <summary>
        /// 为<paramref name="propertyBuilder"/>添加 <typeparamref name="TProperty"/> 转 ShortName String 值转换器
        /// </summary>
        /// <typeparam name="TProperty">nullable enum type</typeparam>
        /// <param name="propertyBuilder">metadata property builder</param>
        /// <returns></returns>
        public static PropertyBuilder<TProperty> HasEnumToShortNameStringNullableConversion<TProperty>(this PropertyBuilder<TProperty> propertyBuilder)
        {
            if (!typeof(TProperty).IsNullableValueType() || !typeof(TProperty).UnwrapNullableType().IsEnum)
            {
                throw new InvalidOperationException("This converter can only be used with nullable enum types.");
            }

            var (toStr, toEnum) = EnumConveterHelper.CreateShortNameExpression<TProperty>();

            return propertyBuilder.HasConversion(new CustomToStringConverter<TProperty>(toStr, toEnum));
        }
    }
}
